<?php

function opt_sort($a, $b)
{
  if ($a["dist"] == $b["dist"]) return 0;
  return ($a["dist"] < $b["dist"]) ? -1 : 1;
}

function parseExplainPerson($p, $query)
{
    $response = "";
    if( $p["_"]["found"] == "true" ) {
        $response .= "<p><strong>".htmlspecialchars($p["_"]["query"])."</strong> can refer to many people:</p>";
    } else {
        $response .= "<p><strong>".htmlspecialchars($p["_"]["query"])."</strong> was not found. Did you mispelled the name?</p>";
    }

    if ( isset($p["option"]) ) {
        if ( trim($p["_"]["query"]) == trim($query["source"]) ) $who = "source";
	else $who = "target";

        $opt = $p["option"];
        $n_options = count($opt);
        if ( $n_options == 1 ) {
            if ( $p["_"]["found"] == "true" )
	        $exact[0] = array("name" => htmlspecialchars($opt["_"]["name"]), 
	  	                  "match" => htmlspecialchars($opt["_"]["match"]),
		                  "dist" => 0);
            else
		$alter[0] = array("name" => htmlspecialchars($opt["_"]["name"]), 
	  	                  "match" => htmlspecialchars($opt["_"]["match"]),
		                  "dist" => 0);
        } else {
          $exc = 0; $alt= 0;
          for($i = 0; $i < $n_options; $i++) {
	      if ( $opt[$i]["_"]["match"] == $p["_"]["query"] )
	          $exact[$exc++] = array("name" => htmlspecialchars($opt[$i]["_"]["name"]),
	     		                 "match" => htmlspecialchars($opt[$i]["_"]["match"]),
                                         "dist" => levenshtein($opt[$i]["_"]["name"], $p["_"]["query"]));
	      else
                  $alter[$alt++] = array("name" => htmlspecialchars($opt[$i]["_"]["name"]),
	     		                 "match" => htmlspecialchars($opt[$i]["_"]["match"]),
                                         "dist" => levenshtein($opt[$i]["_"]["match"], $p["_"]["query"]));
          }
        }

        if ( isset($exact) && count($exact) > 1 )
	    uasort($exact, "opt_sort");
        if ( isset($alter) && count($alter) > 1 )
            uasort($alter, "opt_sort");

	if ( isset($exact) ) {
            $response .= "<ul>";
            foreach ( $exact as $e ) {
		$query[$who] = $e["name"];
		$query[$who."_exact"] = "true";
                $response .= "<li><a style=\"color:#000000;\" href=\"./?".http_build_query($query)."\">".$e["name"]."</a></li>";
	    }
            $response .= "</ul>";
        }

	if ( isset($alter) ) {
	    $response .= "<p>There are some similar names:</p>";
            $response .= "<ul>";
	    foreach ( $alter as $a ) {
		$query[$who] = $a["name"];
		$query[$who."_exact"] = "true";
		$response .= "<li><a style=\"color:#000000;\" href=\"./?".http_build_query($query)."\">".$a["name"]."</a></li>";
	    }
	    $response .= "</ul>";
        }
    } else {
        $response .= "<p>We could not find strong matches to ".htmlspecialchars($p["_"]["query"]).".</p>";
    }
    return $response;
}

abstract class XMLParser {
    protected $array;
    private $parser;
    private $pointer; 

    abstract protected function getResult();
    
    public function __construct($xml) {
        $this->pointer =& $this->array;
        $this->parser = xml_parser_create("UTF-8");
        xml_set_object($this->parser, $this);
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
        xml_set_element_handler($this->parser, "tag_open", "tag_close");
        xml_set_character_data_handler($this->parser, "cdata");
        xml_parse($this->parser, ltrim($xml)); 
        $this->array = $this->array["response"];
    }

    private function tag_open($parser, $tag, $attributes) {
        $this->convert_to_array($tag, '_');
        $idx=$this->convert_to_array($tag, 'cdata');
        if(isset($idx)) {
            $this->pointer[$tag][$idx] = Array('@idx' => $idx,'@parent' => &$this->pointer);
            $this->pointer =& $this->pointer[$tag][$idx];
        }else {
            $this->pointer[$tag] = Array('@parent' => &$this->pointer);
            $this->pointer =& $this->pointer[$tag];
        }
        if (!empty($attributes)) { $this->pointer['_'] = $attributes; }
    }

    private function tag_close($parser, $tag) {
      $current = & $this->pointer;
      if(isset($this->pointer['@idx'])) {unset($current['@idx']);}
      $this->pointer = & $this->pointer['@parent'];
      unset($current['@parent']);
      if(isset($current['cdata']) && count($current) == 1) { $current = $current['cdata'];}
      else if(empty($current['cdata'])) { unset($current['cdata']); } 
    } 

    private function cdata($parser, $cdata) {
        if(isset($this->pointer['cdata'])) { $this->pointer['cdata'] .= $cdata;}
        else { $this->pointer['cdata'] = $cdata;} 
    }

    private function convert_to_array($tag, $item) {
        if(isset($this->pointer[$tag][$item])) {
            $content = $this->pointer[$tag];
            $this->pointer[$tag] = array((0) => $content);
            $idx = 1;
        } else if (isset($this->pointer[$tag])) {
            $idx = count($this->pointer[$tag]);
            if(!isset($this->pointer[$tag][0])) {
                foreach ($this->pointer[$tag] as $key => $value) {
                    unset($this->pointer[$tag][$key]);
                    $this->pointer[$tag][0][$key] = $value;
                }
            }
        } else 
            $idx = null;

        return $idx;
    }

    protected function getExplain($query, $func) 
    {   
        $response = "";
        if ( isset($this->array["explain"]["person"]["0"]) ) {
	   $response .= parseExplainPerson($this->array["explain"]["person"]["0"], $query, $func);
        } else {
            $response .= parseExplainPerson($this->array["explain"]["person"], $query, $func);
        }
        return $response;
    }

    protected function getError()
    {
        return "<p>".htmlspecialchars($this->array["error"]["message"])."</p>";
    }

    public function getResponse($query, $func)
    {
        if ( isset($this->array["explain"]) ) return $this->getExplain($query, $func);
        else if ( isset($this->array["error"]) ) return $this->getError();
        else return $this->getResult();
    }

    public function getArray() 
    {   
        return $this->array;
    }
}

?>
